package com.hulushuju.core.net.http.okhttp;

import com.hulushuju.core.net.ContentType;
import com.hulushuju.core.net.exception.HttpException;
import com.hulushuju.core.net.exception.NullResponseException;
import com.hulushuju.core.net.exception.UnSupportedTypeException;
import com.hulushuju.core.net.http.HttpResponse;
import com.hulushuju.core.util.FileManager;
import okhttp3.MediaType;
import okhttp3.Response;
import okhttp3.ResponseBody;

import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @Author: Hanjiafeng
 * @Date: 2019/5/28
 * @Desc: 根据OkHttp包返回的Response
 */
public class OkHttpResponseImpl implements HttpResponse {

    /**
     * OKHttp.Response
     */
    private Response response;

    OkHttpResponseImpl(Response response) {
        this.response = response;
    }

    @Override
    public long getSendRequestMillis() throws HttpException {
        checkResponse();
        return response.sentRequestAtMillis();
    }

    @Override
    public long getReceivedResponseMillis() throws HttpException {
        checkResponse();
        return response.receivedResponseAtMillis();
    }

    @Override
    public int getStatus() throws HttpException {
        checkResponse();
        return response.code();
    }

    @Override
    public String getRedirectUrl() throws HttpException {
        checkResponse();
        String locationUrl = null;
        //获取响应状态
        boolean isRedirect = response.isRedirect();
        if (isRedirect) {
            locationUrl = response.header("Location");
        }
        return locationUrl;
    }

    @Override
    public ContentType getContentType() throws HttpException {
        checkResponse();
        MediaType mediaType = response.body().contentType();
        String superType = mediaType.type();
        String subType = mediaType.subtype();
        //使用自定义类型设置父类型和子类型
        ContentType contentType = ContentType.CUSTOM;
        contentType.superType(superType).subType(subType);
        return contentType;
    }

    @Override
    public String getHeader(String name) throws HttpException {
        checkResponse();
        return response.header(name);
    }

    @Override
    public Map<String, List<String>> getHeaders() throws HttpException {
        checkResponse();
        return response.headers().toMultimap();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getContent(Class<T> c) throws HttpException {
        checkResponse();
        T content;
        if (c.equals(File.class)) {
            content = (T) getFileBody();
        } else if (c.equals(String.class)) {
            content = (T) getContent();
        } else {
            throw new UnSupportedTypeException("不支持的类型:" + c.getName());
        }

        return content;
    }

    @Override
    public String getCompressContent() throws HttpException {
        checkResponse();
        return null;
    }

    /**
     * 默认直接调用ResponseBody.string()读取String,如果失败,则从流中读取
     *
     * @return String
     */
    private String getContent() {
        ResponseBody responseBody = response.body();
        String content;
        try {
            content = responseBody.string();
        } catch (NullPointerException | IOException | OutOfMemoryError e) {
            //todo 打印异常日志到本地
            e.printStackTrace();
            content = getStringBody();
        }
        return content;
    }

    /**
     * 手动从Response中读取String
     *
     * @return String
     */
    private String getStringBody() {
        ResponseBody responseBody = response.body();
        //获取Charset,认为UTF-8
        String charset;
        try {
            charset = responseBody.contentType().charset().displayName();
        } catch (NullPointerException e) {
            charset = "UTF-8";
        }
        String body = null;
        //从流中读取字节数组
        byte[] bytes = getBytes();
        try (ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream()) {
            //写入字节数组
            byteOutputStream.write(bytes);
            //使用指定字符集buffer的内容转换成String
            body = byteOutputStream.toString(charset);
        } catch (IOException e) {
            //todo 打印异常日志到本地
            e.printStackTrace();
        }

        return body;
    }

    /**
     * 默认从Response.bytes()中读取byte[],如果失败,则从流中读取
     * 然后将byte[]使用Response.charset转换成String,如果Response.charset不存在,默认使用UTF-8
     *
     * @return File
     */
    private File getFileBody() {
        ResponseBody responseBody = response.body();

        byte[] bodyBytes;
        try {
            bodyBytes = responseBody.bytes();
        } catch (IOException | OutOfMemoryError e) {
            //todo 打印异常日志到本地
            e.printStackTrace();
            bodyBytes = getBytes();
        }

        String fileName = FileManager.generateFileName();
        File file = new File(fileName);
        try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
            fileOutputStream.write(bodyBytes);
        } catch (IOException e) {
            //todo 打印异常日志到本地
            e.printStackTrace();
        }

        return file;
    }

    /**
     * 手动从Response中读取byte[]
     *
     * @return byte[]
     */
    private byte[] getBytes() {
        ResponseBody responseBody = response.body();
        byte[] bytes = null;
        try (InputStream inputStream = responseBody.byteStream()) {
            //当前流可用长度
            int inputStreamAvailable = inputStream.available();
            //初始化byte数组
            bytes = new byte[inputStreamAvailable];
            //缓冲区
            byte[] temp = new byte[8192];
            //已经复制到的位置
            int copiedPos = 0;
            while (inputStream.read(temp) != -1) {
                //计算当前剩余字节长度
                int length = inputStreamAvailable - copiedPos;
                //如果比缓冲区长,使用缓冲区长度,否则使用剩余长度
                length = length > temp.length ? temp.length : length;
                //将缓冲区中的字节从第0个开始复制length个到bytes的copiedPos->copiedPos+length位置
                System.arraycopy(temp, 0, bytes, copiedPos, length);
                //计算bytes中当前的复制位置
                copiedPos += temp.length;
            }
        } catch (IOException | OutOfMemoryError e) {
            //todo 打印异常日志到本地
            e.printStackTrace();
        }

        return bytes;
    }

    /**
     * 检查Response是否已经分配
     *
     * @throws NullResponseException 未指定Response
     */
    private void checkResponse() throws NullResponseException {
        if (Objects.isNull(response)) {
            throw new NullResponseException("未指定Response异常");
        }
    }
}